/********************************************************************************************
* MOD-NAME      : SharedMemory.h
* LONG-NAME     : 
*
* AUTHOR        : Martin Ziacek
* DEPARTMENT    : 
* TELEPHONE     : 
* CREATION-DATE : 09/05/2001 18:00:00
* SP-NO         : 
* FUNCTION      : 
* 
*********************************************************************************************/
#pragma once

namespace SharedMemory
{

#define DEF_SHARED_SIZE		128
#define VAR_NAME_LENGTH		40

//structure creates header for a variable
typedef struct _tagValueHeader {
	WCHAR wszValueName[VAR_NAME_LENGTH];		//name of the variable
	DWORD dwLength;								//variable size
} ValueHeader;

class CSharedMemory;

typedef void (*PINITMEMORY)(CSharedMemory *);

class CSingleLock;

class CSyncObject
{
// Constructor
public:
	explicit CSyncObject(LPCTSTR /*pstrName*/)
	{
		m_hObject = NULL;
	}

	virtual ~CSyncObject()
	{
		if (m_hObject != NULL)
		{
			::CloseHandle(m_hObject);
			m_hObject = NULL;
		}
	}

// Attributes
public:
	operator HANDLE() const { return m_hObject; }
	HANDLE  m_hObject;

// Operations
	virtual BOOL Lock(DWORD dwTimeout = INFINITE)
	{
		DWORD dwRet = ::WaitForSingleObject(m_hObject, dwTimeout);
		if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_ABANDONED)
			return TRUE;
		else
			return FALSE;
	}

	virtual BOOL Unlock() = 0;
	
	virtual BOOL Unlock(LONG /* lCount */, LPLONG /* lpPrevCount=NULL */)
		{ return TRUE; }

// Implementation
public:
	friend class CSingleLock;
};


class CSemaphore : public CSyncObject
{
// Constructor
public:
	explicit CSemaphore::CSemaphore(LONG lInitialCount = 1, LONG lMaxCount = 1,
		LPCTSTR pstrName=NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL)
		:  CSyncObject(pstrName)
	{
		ATLASSERT(lMaxCount > 0);
		ATLASSERT(lInitialCount <= lMaxCount);

		m_hObject = ::CreateSemaphore(lpsaAttributes, lInitialCount, lMaxCount, pstrName);
		ATLASSERT(m_hObject != NULL);
	}

	virtual ~CSemaphore() {}

// Attributes
public:

// Operations
	virtual BOOL Unlock() { return Unlock(1, NULL); }

	virtual BOOL Unlock(LONG lCount, LPLONG lpPrevCount=NULL)
	{
		return ::ReleaseSemaphore(m_hObject, lCount, lpPrevCount);
	}
};


class CEvent : public CSyncObject
{
private:
   using CSyncObject::Unlock;

// Constructor
public:
	/* explicit */ CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE,
		LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL)
		: CSyncObject(pstrName)
	{
		m_hObject = ::CreateEvent(lpsaAttribute, bManualReset, bInitiallyOwn, pstrName);
		ATLASSERT(m_hObject != NULL);
	}

	virtual ~CEvent()
	{
	}

// Operations
public:
	BOOL SetEvent() { ATLASSERT(m_hObject != NULL); return ::SetEvent(m_hObject); }
	BOOL PulseEvent() { ATLASSERT(m_hObject != NULL); return ::PulseEvent(m_hObject); }
	BOOL ResetEvent() { ATLASSERT(m_hObject != NULL); return ::ResetEvent(m_hObject); }
	BOOL Unlock() { return TRUE; }
};

class CSingleLock
{
// Constructors
public:
	explicit CSingleLock(CSemaphore* pObject, BOOL bInitialLock = FALSE)
	{
		ATLASSERT(pObject != NULL);
		//	ATLASSERT(pObject->IsKindOf(RUNTIME_CLASS(CSyncObject)));

		//if(pObject == NULL)
		//	AfxThrowInvalidArgException();
			
		m_pObject = pObject;
		m_hObject = pObject->m_hObject;
		m_bAcquired = FALSE;

		if (bInitialLock)
			Lock();
	}


// Operations
public:
	BOOL Lock(DWORD dwTimeOut = INFINITE)
	{
		ATLASSERT(m_pObject != NULL || m_hObject != NULL);
		ATLASSERT(!m_bAcquired);

		m_bAcquired = m_pObject->Lock(dwTimeOut);
		return m_bAcquired;
	}

	BOOL Unlock()
	{
		ATLASSERT(m_pObject != NULL);
		if (m_bAcquired)
			m_bAcquired = !m_pObject->Unlock();

		// successfully unlocking means it isn't acquired
		return !m_bAcquired;
	}

	BOOL Unlock(LONG lCount, LPLONG lpPrevCount = NULL)
	{
		ATLASSERT(m_pObject != NULL);
		if (m_bAcquired)
			m_bAcquired = !m_pObject->Unlock(lCount, lpPrevCount);

		// successfully unlocking means it isn't acquired
		return !m_bAcquired;
	}

	BOOL IsLocked() { return m_bAcquired; }

// Implementation
public:
	~CSingleLock() { Unlock(); }

protected:
	CSemaphore* m_pObject;
	HANDLE  m_hObject;
	BOOL    m_bAcquired;
};


/////////////////////////////////////////////////////////////////////////////
// format of the shared memory
//DWORD		...		variables' count
//DWORD		...		memory size (what was passed to constructor, it means whole memory size
//ValueHeader[0]
//value 0
//ValueHeader[1]
//value 1
// .....
//ValueHeader[n]
//value n

/////////////////////////////////////////////////////////////////////////////
// CSharedMemory class
class CSharedMemory //: public CObject
{
public:
	enum {
		MAX_VAR_NAME_LENGTH = VAR_NAME_LENGTH,
	};
	//DECLARE_DYNAMIC(CSharedMemory)

	CSharedMemory(const TCHAR *szName, DWORD dwSize = DEF_SHARED_SIZE, PINITMEMORY InitMemoryProcedure = NULL,LPSECURITY_ATTRIBUTES lpsaAttributes = NULL);
	~CSharedMemory();

	CString GetMemName(void);

	BOOL SetSdMem(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor);
	BOOL GetSdMem(SECURITY_INFORMATION RequestedInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength, LPDWORD lpnLengthNeeded);
	BOOL SetSdSem(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor);
	BOOL GetSdSem(SECURITY_INFORMATION RequestedInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength, LPDWORD lpnLengthNeeded);
	BOOL SetSa(LPSECURITY_ATTRIBUTES lpsaAttributes);

	BOOL AddValue(const TCHAR *szName, DWORD size, void *pDefaultData = NULL);
	BOOL AddDwordValue(const TCHAR *szName, DWORD dwDefault = 0);
	BOOL DeleteValue(const TCHAR *szName);

	BOOL ExistValue(const TCHAR *szName);
	BOOL IsCreated(void);

	DWORD GetVariablesCount(void);
	BOOL GetValueInfo(DWORD dwIndex, ValueHeader *pVarInfo);

	BOOL SetValue(const TCHAR *szName, void *bData, DWORD dwLength);
	BOOL GetValue(const TCHAR *szName, void *bData, LPDWORD dwLength);

	BOOL SetDwordValue(const TCHAR *szName, DWORD dwVal);
	DWORD GetDwordValue(const TCHAR *szName, DWORD dwDefVal = -1);

	void *GetPointer(const TCHAR *szName);

	DWORD WaitForMemChange(DWORD dwMilliseconds = INFINITE);
	DWORD WaitForValueChange(const TCHAR *szName, DWORD dwMilliseconds = INFINITE);
	//DWORD WaitForMultipleValuesChanges(CStringArray &str, BOOL bWaitForAll = TRUE, DWORD dwMilliseconds = INFINITE);
	DWORD WaitForValueChange(const TCHAR *szName, DWORD oldValue, LPDWORD newValue,
		HANDLE closeEvent, HANDLE cancelEvent, DWORD dwMilliseconds = INFINITE);

	BOOL InterlockedIncrement(const TCHAR *szName, LPLONG plNewVal = NULL);
	BOOL InterlockedDecrement(const TCHAR *szName, LPLONG plNewVal = NULL);
	BOOL InterlockedExchange(const TCHAR *szTargetName, LONG lNewVal, LPLONG plPrevValue = NULL);
	BOOL InterlockedTestExchange(const TCHAR *szTargetName, LONG lOldValue, LONG lNewValue, LPLONG plPrevValue = NULL);
	BOOL InterlockedCompareExchange(const TCHAR *szTargetName, LONG lExchange, LONG lComperand, LPLONG plIntiVal = NULL);
	BOOL InterlockedExchangeAdd(const TCHAR *szTargetName, LONG lIncrement, LPLONG plIntiVal = NULL);

	BOOL InterlockedTestIncrement(const TCHAR *szName, LONG notEqualValue, LPLONG plNewVal = NULL);
	BOOL InterlockedTestDecrement(const TCHAR *szName, LONG notEqualValue, LPLONG plNewVal = NULL);

	BOOL Write(BYTE *pbData, DWORD dwLength, DWORD dwOffset = 0);
	BOOL Read(BYTE *pbData, DWORD dwLength, DWORD dwOffset = 0);

#ifdef _DEBUG
	virtual void AssertValid(void);
	//virtual void Dump(CDumpContext & dc);
	virtual void Dump();
#endif

	DWORD GetLastError(void);
	DWORD GetMemSize(void);
	BOOL AmIFirst(void);

protected:
	void SetLastError(DWORD dwErrCode);
	BOOL CreateSa(void);
	BOOL FindValue(const WCHAR *wszName,LPBYTE *pData, ValueHeader **pTmp);
	WCHAR *AllocateUnicodeStr(const char *szStr);

	CString GetMemoryChangeEventName();
	CString GetVariableChangeEventName(const wchar_t* varName);
	void FireMemoryChange();
	void FireVariableChange(const wchar_t* varName);

	BOOL InterlockedTestIncrementDecrement(const TCHAR *szName, BOOL increment, LONG notEqualValue, LPLONG plNewVal = NULL);

	CString m_CsName;			//name of shared memory

	DWORD m_dwLastError;		//last error issued from system by method of class

	CSemaphore *m_pSync;		//synchronization of shared memory - across processes
	CSingleLock *m_pLock;		//is made by these two objects

	BOOL m_bFirst;				//TRUE, if this is first reference to sm

	BOOL m_bCreated;			//TRUE, if shared memory was created

	DWORD m_dwMemSize;			//size of allocated shared memory

	LPBYTE m_lpView;			//pointer to the begining of shared memory
	HANDLE m_hFileMap;			//handle of shared memory (mapped file)

	BOOL m_bSecPres;			//TRUE, if OS support security

	UCHAR m_SdEvent[SECURITY_DESCRIPTOR_MIN_LENGTH];
	SECURITY_ATTRIBUTES m_SecAtr;
	SID *m_pLogSid;
	SID *m_pSysSid;
	ACL *m_pNewAcl;
	SID *m_pEvrSid;
};

}
